#from src.projectile import owner
from pandac.PandaModules import *    #basic Panda modules
from direct.actor.Actor import Actor #for animated models
from direct.task import Task         #for update fuctions
from panda3d.core import CollisionHandlerQueue,CollisionRay,BitMask32             #for collisions with the environment
import sys, math, random
from FollowCam import FollowCam
import cfg
from turret import Turret

class Player(object):     #not sure if the player is going to accept events so no subclassing from DirectObject yet
    """Just making the player display and move, and writing other fuctions that we might need (shoot, jump?, etc.)"""
    
    def __init__(self):
        self.firingmode = 0
        self.modes = ["laser", "plasma", "acid"]
        self.loadModel()
        self.setupLights()
        #self.setupCollisions()
        self.keyMap = {"left":0, "right":0, "forward":0, "back":0, "shoot":0}
        taskMgr.add(self.move, "moveTask")
        taskMgr.add(self.shoot, "shootTask")
        #self.accept("bullet-hit-player", self.hit)
        self.vz = 0
        self.onGround = False
        self.az = -.03
        self.health = 100
        self.prevtime = 0
        self.isMoving = False
        self.prevspeed = 0
        self.prevR = 0
        self.prevL = 0
        self.prevF = 0
        self.prevB = 0
        self.vz = 0
        self.az = 0
        self.cooldown = .4
        self.cooldowncounter = 0

    def cycleMode(self, m):
        self.firingmode = (self.firingmode + m) % 3

    def setMode(self, m):
        self.firingmode = m
            
    def setKey(self, key, value):
        self.keyMap[key] = value
        
    def loadModel(self):
        self.model = loader.loadModel("../models/player.egg")
        self.model.setTexture(loader.loadTexture("../textures/playertexture.jpg"))
        ts = TextureStage('ts')
        ts.setMode(TextureStage.MModulateGlow)
        self.model.setTexture(ts, loader.loadTexture("../textures/player_glow.jpg"))
        self.model.reparentTo(render)
        self.model.setScale(.5)      #if needed
        #player turret
        self.turret = Turret("player", self.modes[self.firingmode])
        self.turret.model.setScale(1)
        self.turret.model.setPos(0,0,1)
        self.turret.model.reparentTo(self.model)

        #n_dist = math.sqrt(math.pow(self.model.getX(), 2) + math.pow(self.model.getY() + 30, 2))

        self.hitNode = self.model.attachNewNode("hit-node")
        #self.hitNode.reparentTo(self.model)
        self.hitNode.setPos(0, -5000, 100)
        # self.myShow.cam.node().getDisplayRegion(0).setActive(0) #turn off main camera
        # self.cameras = [self.myShow.makeCamera(self.myShow.win), self.myShow.makeCamera(self.myShow.win)]
        # #Front-facing camera
        # self.cameras[0].(self.model)
        # self.cameras[0].setPosHpr(0,2000,1000,180,-15,0)
        # #Rear-view camera
        # self.cameras[1].reparentTo(self.model)
        # self.cameras[1].setPosHpr(0,-2000,1000,0,-15,0)
        # self.cameras[1].node().getDisplayRegion(0).setActive(0)
        
    def setupLights(self):
        """Create both headlights, attach them to the model, and set them to light the whole scene"""
        # self.rightHeadlightM = loader.loadModel("../models/headlightGlow")
        # self.rightHeadlightM.reparentTo(self.model)
        # self.rightHeadlightM.setPos(-250, -500, 250)
        # self.rightHeadlightM.setScale(10)
        # self.leftHeadlightM = loader.loadModel("../models/headlightGlow")
        # self.leftHeadlightM.reparentTo(self.model)
        # self.leftHeadlightM.setPos(250, -500, 250)
        # self.leftHeadlightM.setScale(10)
        
        lens = PerspectiveLens()
        self.rightHeadlight = Spotlight("rightHeadlight")
        self.rightHeadlight.setColor((10,10,10,1))
        self.rightHeadlight.setLens(lens)
        self.rightHeadlight.setShadowCaster(True, 512, 512)
        #self.rightHeadlightNP = self.rightHeadlightM.attachNewNode(self.rightHeadlight)
        self.rightHeadlightNP = self.model.attachNewNode(self.rightHeadlight)
        self.rightHeadlightNP.setPos(7, 5, 10)
        self.rightHeadlightNP.setHpr(180, -5, 0)
        
        self.leftHeadlight = Spotlight("leftHeadlight")
        self.leftHeadlight.setColor((10,10,10,1))
        self.leftHeadlight.setLens(lens)
        self.leftHeadlight.setShadowCaster(True, 512, 512)
        #self.leftHeadlightNP = self.leftHeadlightM.attachNewNode(self.leftHeadlight)
        self.leftHeadlightNP = self.model.attachNewNode(self.leftHeadlight)
        self.leftHeadlightNP.setPos(-7, 5, 10)
        self.leftHeadlightNP.setHpr(180, -5, 0)
        
        render.setLight(self.rightHeadlightNP)
        render.setLight(self.leftHeadlightNP)
        
    def setupCollisions(self):
        self.gRay1 = CollisionSegment(0,2,1000,0,2,-1000)
        #self.gRay1.setEffectiveNormal(Vec3(0,0,1))
        # self.gRay.setOrigin(0,0,1000)
        # self.gRay.setDirection(0, 0, -1)
        self.gCol1 = CollisionNode("playerRay1")
        self.gCol1.addSolid(self.gRay1)
        self.gCol1.setFromCollideMask(BitMask32.bit(0))
        self.gCol1.setIntoCollideMask(BitMask32.allOff())
        self.gColNp1 = self.model.attachNewNode(self.gCol1)
        self.gHandler = CollisionHandlerQueue()
        
        base.cTrav.addCollider(self.gColNp1, self.gHandler)
        #self.gColNp1.show()
        
    def resetMouse(self):
        cx = base.win.getProperties().getXSize() / 2
        cy = base.win.getProperties().getYSize() / 2
        base.win.movePointer(0, cx, cy)
    
    def move(self, task):
        if self.model.getX() > 101:
            self.model.setX(100)
        if self.model.getY() > 101:
            self.model.setY(100)
        if self.model.getX() < -101:
            self.model.setX(-100)
        if self.model.getY() < -101:
            self.model.setY(-100)
        self.cooldowncounter += globalClock.getDt()

        if base.mouseWatcherNode.hasMouse():
            self.model.setH(self.model, -base.mouseWatcherNode.getMouseX() * 30)
            self.resetMouse()
        
        elapsed = (task.time - self.prevtime)  #self.prevtime
        startpos = self.model.getPos()

        self.vz += self.az
        #print self.vz
        self.model.setZ(self.model.getZ() + self.vz * elapsed*4)

        #self.vz += self.az + cfg.gravity
        #self.az *= .9
        #self.model.setZ(self.model, self.vz)
        
        # self.prevspeed = min(cfg.playerspeed, elapsed*cfg.playeracceleration + self.prevspeed)
        # dist = elapsed * self.prevspeed
        #print str(self.prevspeed)


        if self.keyMap["left"] and not self.keyMap["right"]:
            self.prevL = min(cfg.playerspeed, elapsed*cfg.playeracceleration + self.prevL)
            dist = elapsed * self.prevL
            self.prevR = 0
            angle = deg2Rad(self.model.getH())
            dx = dist * math.sin(angle + (math.pi/2))
            dy = dist * -math.cos(angle + (math.pi/2))
            self.model.setX(self.model.getX()+dx)
            self.model.setY(self.model.getY()+dy)
            #print self.model.getX(), self.model.getY()
            if not self.keyMap["forward"] and not self.keyMap["back"]:
                if self.prevF > 0:
                    self.prevF = max(0, elapsed*cfg.playerfriction + self.prevF)
                    dist = elapsed * self.prevF
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle)
                    dy = dist * -math.cos(angle)
                    self.model.setX(self.model.getX()+dx)
                    self.model.setY(self.model.getY()+dy)
                elif self.prevB > 0:
                    self.prevB = max(0, elapsed*cfg.playerfriction + self.prevB)
                    dist = elapsed * self.prevB
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle)
                    dy = dist * -math.cos(angle)
                    self.model.setPos((self.model.getX()-dx, self.model.getY()-dy, self.model.getZ()))

        if self.keyMap["right"] and not self.keyMap["left"]:
            self.prevR = min(cfg.playerspeed, elapsed*cfg.playeracceleration + self.prevR)
            dist = elapsed * self.prevR
            self.prevL = 0
            angle = deg2Rad(self.model.getH())
            dx = dist * math.sin(angle - (math.pi/2))
            dy = dist * -math.cos(angle - (math.pi/2))
            self.model.setX(self.model.getX()+dx)
            self.model.setY(self.model.getY()+dy)
            #print self.model.getX(), self.model.getY()
            if not self.keyMap["forward"] and not self.keyMap["back"]:
                if self.prevF > 0:
                    self.prevF = max(0, elapsed*cfg.playerfriction + self.prevF)
                    dist = elapsed * self.prevF
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle)
                    dy = dist * -math.cos(angle)
                    self.model.setX(self.model.getX()+dx)
                    self.model.setY(self.model.getY()+dy)
                elif self.prevB > 0:
                    self.prevB = max(0, elapsed*cfg.playerfriction + self.prevB)
                    dist = elapsed * self.prevB
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle)
                    dy = dist * -math.cos(angle)
                    self.model.setPos((self.model.getX()-dx, self.model.getY()-dy, self.model.getZ()))

        if self.keyMap["forward"] and not self.keyMap["back"]:
            self.prevF = min(cfg.playerspeed, elapsed*cfg.playeracceleration + self.prevF)
            dist = elapsed * self.prevF
            self.prevB = 0
            angle = deg2Rad(self.model.getH())
            dx = dist * math.sin(angle)
            dy = dist * -math.cos(angle)
            self.model.setX(self.model.getX()+dx)
            self.model.setY(self.model.getY()+dy)
            #self.model.setY(self.model, -20)
            if not self.keyMap["left"] and not self.keyMap["right"]:
                if self.prevL > 0:
                    self.prevL = max(0, elapsed*cfg.playerfriction + self.prevL)
                    dist = elapsed * self.prevL
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle + (math.pi/2))
                    dy = dist * -math.cos(angle + (math.pi/2))
                    self.model.setX(self.model.getX()+dx)
                    self.model.setY(self.model.getY()+dy)
                elif self.prevR > 0:
                    self.prevR = max(0, elapsed*cfg.playerfriction + self.prevR)
                    dist = elapsed * self.prevR
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle - (math.pi/2))
                    dy = dist * -math.cos(angle - (math.pi/2))
                    self.model.setX(self.model.getX()+dx)
                    self.model.setY(self.model.getY()+dy)
            
        if self.keyMap["back"] and not self.keyMap["forward"]:
            #turn backlights on
            self.prevB = min(cfg.playerspeed, elapsed*cfg.playeracceleration + self.prevB)
            dist = elapsed * self.prevB
            self.prevF = 0
            angle = deg2Rad(self.model.getH())
            dx = dist * math.sin(angle)
            dy = dist * -math.cos(angle)
            self.model.setX(self.model.getX()-dx)
            self.model.setY(self.model.getY()-dy)
            #self.model.setPos((self.model.getX()-dx, self.model.getY()-dy, self.model.getZ()))
            #self.model.setY(self.model, 20)
            if not self.keyMap["left"] and not self.keyMap["right"]:
                if self.prevL > 0:
                    self.prevL = max(0, elapsed*cfg.playerfriction + self.prevL)
                    dist = elapsed * self.prevL
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle + (math.pi/2))
                    dy = dist * -math.cos(angle + (math.pi/2))
                    self.model.setX(self.model.getX()+dx)
                    self.model.setY(self.model.getY()+dy)
                elif self.prevR > 0:
                    self.prevR = max(0, elapsed*cfg.playerfriction + self.prevR)
                    dist = elapsed * self.prevR
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle - (math.pi/2))
                    dy = dist * -math.cos(angle - (math.pi/2))
                    self.model.setX(self.model.getX()+dx)
                    self.model.setY(self.model.getY()+dy)
        
        if (self.keyMap["left"] or self.keyMap["right"] or self.keyMap["forward"] or self.keyMap["back"]) and (self.prevL > 0 or self.prevR > 0 or self.prevF > 0 or self.prevB > 0):
            if (self.keyMap["left"] and self.keyMap["right"]) or (self.keyMap["forward"] and self.keyMap["back"]):
                if self.isMoving:
                    self.isMoving = False
                    self.prevL = 0
                    self.prevR = 0
                    self.prevF = 0
                    self.prevB = 0
                    #self.model.stop()
                    #self.model.pose("drive", 4)
            elif self.isMoving == False:
                self.isMoving = True
                #self.model.loop("drive")
        else:
            if self.isMoving:
                #print "Here"
                if self.prevF > 0:
                    self.prevF = max(0, elapsed*cfg.playerfriction + self.prevF)
                    dist = elapsed * self.prevF
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle)
                    dy = dist * -math.cos(angle)
                    self.model.setX(self.model.getX()+dx)
                    self.model.setY(self.model.getY()+dy)
                elif self.prevB > 0:
                    self.prevB = max(0, elapsed*cfg.playerfriction + self.prevB)
                    dist = elapsed * self.prevB
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle)
                    dy = dist * -math.cos(angle)
                    self.model.setPos((self.model.getX()-dx, self.model.getY()-dy, self.model.getZ()))
                    
                if self.prevL > 0:
                    self.prevL = max(0, elapsed*cfg.playerfriction + self.prevL)
                    dist = elapsed * self.prevL
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle + (math.pi/2))
                    dy = dist * -math.cos(angle + (math.pi/2))
                    self.model.setX(self.model.getX()+dx)
                    self.model.setY(self.model.getY()+dy)
                elif self.prevR > 0:
                    self.prevR = max(0, elapsed*cfg.playerfriction + self.prevR)
                    dist = elapsed * self.prevR
                    angle = deg2Rad(self.model.getH())
                    dx = dist * math.sin(angle - (math.pi/2))
                    dy = dist * -math.cos(angle - (math.pi/2))
                    self.model.setX(self.model.getX()+dx)
                    self.model.setY(self.model.getY()+dy)
                    
                if  (self.prevL == 0 and self.prevR == 0 and self.prevF == 0 and self.prevB == 0):
                    self.isMoving = False
                    #self.model.stop()
                    #self.model.pose("drive", 4)
        
        #Ground Collision
        base.cTrav.traverse(render)
        entries = []
        
        for i in range(self.gHandler.getNumEntries()):
            entry = self.gHandler.getEntry(i)
            entries.append(entry)
            
        entries.sort(lambda a, b: cmp(a.getSurfacePoint(render).getZ(), b.getSurfacePoint(render).getZ()))
        
        if (len(entries)>0) and (entries[0].getIntoNode().getName() == "collisionPlane"):
            z1 = entries[0].getSurfacePoint(render).getZ()
            if z1 > startpos.getZ():
                self.model.setZ( z1 )
                self.vz = 0
                self.az = 0
            else:
                self.az = -.02
            self.onGround = True
        else:
            self.az = -.02
            self.model.setX(startpos.getX())
            self.model.setY(startpos.getY())
            

            # print str(dz) + " " + str(dy) + " " + str(math.degrees(angle))
            #self.model.setHpr(entries1[0].getIntoNodePath(), self.model.getH(),0,0)
            #self.model.setHpr(render, 0,self.gRay1.getEffectiveNormal().signedAngleDeg(entries1[0].getSurfaceNormal(self.model), Vec3(1,0,0)), 0)
        # else:
            # print "No hit"
            # #self.model.setPos(startpos)
        
        
        self.prevtime = task.time
        return task.cont
        
    def shoot(self, task):
        if self.keyMap["shoot"] and self.cooldowncounter > self.cooldown:
            self.cooldowncounter = 0
            self.turret.shoot(self.modes[self.firingmode])
        return task.cont
        
    def jump(self):
        if self.onGround:
            self.vz = 1.85
            self.onGround = False
    
    def changeZ(self, cEntry):
        self.model.setZ(cEntry.getSurfacePoint(render).getZ())

    def plasmaHit(self, cEntry):
        cfg.projectilelist.remove(cEntry.getFromNodePath().getParent())
        cEntry.getFromNodePath().getParent().remove()
        self.damage(cfg.plasmaDmg*5)

    def laserHit(self, cEntry):
        cfg.projectilelist.remove(cEntry.getFromNodePath().getParent())
        cEntry.getFromNodePath().getParent().remove()
        self.damage(cfg.laserDmg*3)

    def acidHit(self, cEntry):
        cfg.projectilelist.remove(cEntry.getFromNodePath().getParent())
        cEntry.getFromNodePath().getParent().remove()
        self.damage(cfg.acidDmg)
        
    def damage(self, dam):
        """Returns True if the player dies from the damage"""
        snd = base.loader.loadSfx("../sounds/player_damage.wav")
        snd.setVolume(.2)
        snd.play()
        if self.health - dam <= 0:
            self.health = 0
            #sys.exit()
            #return True
        else:
            self.health -= dam
            #return False

            